# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# See LICENSE for more details.
#
# Copyright: Red Hat Inc. 2014
# Author: Lucas Meneghel Rodrigues <lmr@redhat.com>
# Author: Lukas Doktor <ldoktor@redhat.com>
"""
This file contains tools for (not only) Avocado developers.
"""
import logging
import os
import time

# Use this for debug logging
LOGGER = logging.getLogger(__name__)

# measure_duration global storage
__MEASURE_DURATION = {}


def measure_duration(func):
    """
    Use this as decorator to measure duration of the function execution.
    The output is "Function $name: ($current_duration, $accumulated_duration)"
    """

    def wrapper(*args, **kwargs):
        """Wrapper function"""
        start = time.monotonic()
        try:
            return func(*args, **kwargs)
        finally:
            duration = time.monotonic() - start
            __MEASURE_DURATION[func] = __MEASURE_DURATION.get(func, 0) + duration
            LOGGER.debug(
                "PERF: %s: (%.9fs, %.9fs)", func, duration, __MEASURE_DURATION[func]
            )

    return wrapper


def log_calls_class(length=None):
    """
    Use this as decorator to log the function methods' calls.
    :param length: Max message length
    """

    def wrap(orig_cls):
        for key, attr in orig_cls.__dict__.items():
            if callable(attr):
                setattr(orig_cls, key, _log_calls(attr, length, orig_cls.__name__))
        return orig_cls

    return wrap


def _log_calls(func, length=None, cls_name=None):
    """
    log_calls wrapper function
    """

    def wrapper(*args, **kwargs):
        """Wrapper function"""
        msg = "CALL: %s:%s%s(%s, %s)" % (  # pylint: disable=C0209
            os.path.relpath(func.__code__.co_filename),
            cls_name,
            func.__name__,
            ", ".join([str(_) for _ in args]),
            ", ".join(
                [
                    f"{key}={value}"  # pylint: disable=C0209
                    for key, value in kwargs.items()
                ]
            ),
        )
        if length:
            msg = msg[:length]
        LOGGER.debug(msg)
        return func(*args, **kwargs)

    if cls_name:
        cls_name = cls_name + "."
    return wrapper


def log_calls(length=None, cls_name=None):
    """
    Use this as decorator to log the function call altogether with arguments.
    :param length: Max message length
    :param cls_name: Optional class name prefix
    """

    def wrap(func):
        return _log_calls(func, length, cls_name)

    return wrap
